/***************************************************************************
 *   Copyright (C) 2007 by Ben Barsdell   *
 *   bb_vb@hotmail.com   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#ifndef Vector3D_H
#define Vector3D_H

//Standard library headers
#include <math.h>
#include <iostream>

#define PI 3.14159265358979323846

//Operator definitions
//  Cross pruduct = 	^
//  Dot product =   	%
//  2D cross product = 	*

class Vector3D;

//std::ostream& operator <<(std::ostream &os, Vector3D& obj);

class Vector3D
{

    public:

	//Scaler type definition
	typedef double scalar;

    private:
    
    //Array to hold vector data
	scalar V[3];
	
    public:

	//Initalisation constructor
	Vector3D(scalar X = 0, scalar Y = 0, scalar Z = 0) { V[0]=X; V[1]=Y; V[2]=Z; }
	
	
	//Array access operator
	scalar operator [] (const unsigned i) const	{ return V[i]; }
	scalar &operator [] (const unsigned i) { return V[i]; }
	
	//Copy constructor
	Vector3D(const Vector3D &vec) { V[0]=vec[0]; V[1]=vec[1]; V[2]=vec[2]; }
	
	//Function to return vector array
	scalar* getArray()					{ return V; }
	
	//Element access functions
	scalar x()	const				{ return V[0]; }
	scalar y()	const				{ return V[1]; }
	scalar z()	const				{ return V[2]; }
	scalar &x()						{ return V[0]; }
	scalar &y()						{ return V[1]; }
	scalar &z()						{ return V[2]; }
	
	//friend std::ostream& operator <<(std::ostream &os,const Vector3D &obj);
	
	//Vector assignment
	const Vector3D &operator = (const Vector3D &vec)
	{
	    V[0] = vec[0];
	    V[1] = vec[1];
	    V[2] = vec[2];
		
	    return *this;
	}
	
	//Scalar assignment
	const Vector3D &operator = (const scalar &s)
	{
	    V[0] = s;
	    V[1] = s;
	    V[2] = s;
		
	    return *this;
	}
	
	//Vector equality
	const bool operator == (const Vector3D &vec) const
	{
	    return ((V[0] == vec[0]) && (V[1] == vec[1]) && (V[2] == vec[2]));
	}
	
	//Vector inequality
	const bool operator != (const Vector3D &vec) const
	{
	    return !(*this == vec);
	}
	
	//Vector add
	const Vector3D operator + (const Vector3D &vec) const
	{
	    return Vector3D(V[0] + vec[0], V[1] + vec[1], V[2] + vec[2]);
	}
	
	//Vector add (opposite of negation)
	const Vector3D operator + () const
	{    
	    return Vector3D(*this);
	}
	
	//Vector increment
	const Vector3D& operator += (const Vector3D& vec)
	{    
	    V[0] += vec[0];
	    V[1] += vec[1];
	    V[2] += vec[2];
	  	
	    return *this;
	}
	
	//Vector subtraction
	const Vector3D operator - (const Vector3D& vec) const
	{    
	    return Vector3D(V[0] - vec[0], V[1] - vec[1], V[2] - vec[2]);
	}
	
	//Vector negation
	const Vector3D operator - () const
	{    
	    return Vector3D(-V[0], -V[1], -V[2]);
	}
	
	//Vector decrement
	const Vector3D &operator -= (const Vector3D& vec)
	{
	    V[0] -= vec[0];
	    V[1] -= vec[1];
	    V[2] -= vec[2];
	
	    return *this;
	}
	
	//scalar self-multiply
	const Vector3D &operator *= (const scalar &s)
	{
	    V[0] *= s;
	    V[1] *= s;
	    V[2] *= s;
	  
	    return *this;
	}
	
	//Scalar self-divide
	const Vector3D &operator /= (const scalar &s)
	{
	    const scalar recip = 1/s; // for speed, one divecision
	
	    V[0] *= recip;
	    V[1] *= recip;
	    V[2] *= recip;
	
	    return *this;
	}
	
	//Post multiply by scalar
	const Vector3D operator * (const scalar &s) const
	{
	    return Vector3D(V[0]*s, V[1]*s, V[2]*s);
	}
	
	//Pre multiply by scalar
	friend inline const Vector3D operator*(const scalar &s, const Vector3D &vec)
	{
	    return vec*s;
	}
	
	//Divide by scalar
	const Vector3D operator / (scalar s) const
	{
	    s = 1/s;
	
	    return Vector3D(s*V[0], s*V[1], s*V[2]);
	}
	
	//Cross product
	const Vector3D CrossProduct(const Vector3D &vec) const
	{
	    return Vector3D(V[1]*vec[2] - V[2]*vec[1], V[2]*vec[0] - V[0]*vec[2], V[0]*vec[1] - V[1]*vec[0]);
	}
	
	//Cross product
	const Vector3D operator ^ (const Vector3D &vec) const
	{
	    return Vector3D(V[1]*vec[2] - V[2]*vec[1], V[2]*vec[0] - V[0]*vec[2], V[0]*vec[1] - V[1]*vec[0]);
	}
	
	//Dot product
	const scalar DotProduct(const Vector3D &vec) const
	{
	    return V[0]*vec[0] + V[1]*vec[1] + V[2]*vec[2];
	}
	
	//Dot product
	const scalar operator % (const Vector3D &vec) const
	{
	    return V[0]*vec[0] + V[1]*vec[1] + V[2]*vec[2];
	}
	
	//2D cross product
	const scalar operator * (const Vector3D &vec) const
	{
	    return (V[0]*vec[1] - V[1]*vec[0]);
	}
	
	//Length of vector
	const scalar Length() const
	{
	    return (scalar)sqrt((double)(V[0]*V[0] + V[1]*V[1] + V[2]*V[2]));
	}
	
	//Length squared of vector
	const scalar Length2() const
	{
	    return (scalar)(double)(V[0]*V[0] + V[1]*V[1] + V[2]*V[2]);
	}
	
	//Return the unit vector
	const Vector3D UnitVector() const
	{
	    return (*this) / Length();
	}
	
	//Return the 2D normal vector
	const Vector3D Normal() const
	{
	    return Vector3D(V[1], -V[0], 0);
	}
	
	//Normalise this vector
	void Normalize()
	{
	    (*this) /= Length();
	}
	
	//Return angle of vector in degrees
	const scalar Angle() const
	{
		
	    if (V[0] == 0){
		if (V[1] > 0)
		    return -90;
		else
		    return 90;
	    }
		
	    scalar a = (scalar)atan((double)(V[1] / V[0])) * 180 / PI;
		
	    if (V[0] > 0.0f)
		return (a + 180);
	    else
		return a;
	}
	
	//Zero the vector 
	void Zero()
	{
		
	    V[0] = 0;
	    V[1] = 0;
	    V[2] = 0;
		
	}
	
	/*//Return vector with specified length
	const Vector3D operator | (const scalar length) const
	{
	return *this * (length / !(*this));
}*/
	
	/*// set length of Vector3D equal to length
	const Vector3D& operator |= (const float length)
	{
	return *this = *this | length;
}*/
	
	// return angle between two vectors
	/*const float inline Angle(const Vector3D& normal) const
	{
	return acosf(*this % normal);
}*/
	
	// reflect this vector off surface with normal vector
	/*const Vector3D inline Reflection(const Vector3D& normal) const
	{
	const Vector3D vec(*this | 1);     // normaliV[2]e this Vector3D
	return (vec - normal * 2.0 * (vec % normal)) * !*this;
}*/

};

//Stream operators
/*std::ostream& operator <<(std::ostream &os, Vector3D& obj)
{
      os << obj[0] << ", " << obj[1] << ", " << obj[2];
      return os;
}*/

#endif
